const Cpu = @This();

/// The process running on this cpu, or null.
process: ?*Proc = null,
/// swtch() here to enter scheduler().
context: Context = .{},
/// Depth of push() nesting.
depth: usize = 0,
/// Were interrupts enabled before push()?
interrupt: bool = false,

var cpus: [os.NCPU]Cpu = @splat(.{});

/// Return this CPU's cpu struct. Interrupts must be disabled.
pub fn this() *Cpu {
    return &cpus[id()];
}

/// Must be called with interrupts disabled, to prevent race with process being moved to a different CPU.
pub fn id() usize {
    return gpr.read(.tp);
}

/// Disables interrupts and increments nest level. First call saves original interrupt state. Must be
/// matched with exactly one pop().
pub fn push() *Cpu {
    // disable interrupts to prevent an involuntary context switch while using Cpu.this().
    const old = os.arch.intr.disable();
    const cpu = this();
    if (cpu.depth == 0) cpu.interrupt = old;
    cpu.depth += 1;
    return cpu;
}

/// Restores interrupts when nest level reaches zero. Panics if interrupts enabled or calls unbalanced.
pub fn pop(cpu: *Cpu) void {
    if (os.arch.intr.get()) @panic("interruptible");
    if (cpu.depth < 1) @panic("unbalanced");

    cpu.depth -= 1;
    if (cpu.depth == 0 and cpu.interrupt) os.arch.intr.enable();
}

const os = @import("../os.zig");
const gpr = @import("riscv").gpr;
const Proc = @import("Proc.zig");
const Context = @import("context.zig").Context;
